<?php

namespace We7\Http;

use InvalidArgumentException;
use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\StreamInterface;
use Psr\Http\Message\UriInterface;

class Request extends Message implements RequestInterface {


	/** @var string */
	private $method;

	/** @var null|string */
	private $requestTarget;

	/** @var UriInterface */
	private $uri;

	/**
	 * @param string                               $method  HTTP method
	 * @param string|UriInterface                  $uri     URI
	 * @param array                                $headers Request headers
	 * @param string|null|resource|StreamInterface $body    Request body
	 * @param string                               $version Protocol version
	 */
	public function __construct(
		$method,
		$uri,
		array $headers = array(),
		$body = null,
		$version = '1.1'
	) {
		if (!($uri instanceof UriInterface)) {
			$uri = new Uri($uri);
		}

		$this->method = strtoupper($method);
		$this->uri = $uri;
		$this->setHeaders($headers);
		$this->protocol = $version;

		if (!$this->hasHeader('Host')) {
			$this->updateHostFromUri();
		}

		if ($body !== '' && $body !== null) {
			$this->stream = stream_for($body);
		}
	}

	public function getRequestTarget()
	{
		if ($this->requestTarget !== null) {
			return $this->requestTarget;
		}

		$target = $this->uri->getPath();
		if ($target == '') {
			$target = '/';
		}
		if ($this->uri->getQuery() != '') {
			$target .= '?' . $this->uri->getQuery();
		}

		return $target;
	}

	public function withRequestTarget($requestTarget)
	{
		if (preg_match('#\s#', $requestTarget)) {
			throw new InvalidArgumentException(
				'Invalid request target provided; cannot contain whitespace'
			);
		}

		$new = clone $this;
		$new->requestTarget = $requestTarget;
		return $new;
	}

	public function getMethod()
	{
		return $this->method;
	}

	public function withMethod($method)
	{
		$new = clone $this;
		$new->method = strtoupper($method);
		return $new;
	}

	public function getUri()
	{
		return $this->uri;
	}

	public function withUri(UriInterface $uri, $preserveHost = false)
	{
		if ($uri === $this->uri) {
			return $this;
		}

		$new = clone $this;
		$new->uri = $uri;

		if (!$preserveHost) {
			$new->updateHostFromUri();
		}

		return $new;
	}

	private function updateHostFromUri()
	{
		$host = $this->uri->getHost();

		if ($host == '') {
			return;
		}

		if (($port = $this->uri->getPort()) !== null) {
			$host .= ':' . $port;
		}

		if (isset($this->headerNames['host'])) {
			$header = $this->headerNames['host'];
		} else {
			$header = 'Host';
			$this->headerNames['host'] = 'Host';
		}
		// Ensure Host is the first header.
		// See: http://tools.ietf.org/html/rfc7230#section-5.4
		$this->headers = array($header => array($host)) + $this->headers;
	}


}
